﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Darkmoon.DX11Imported;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {
            System.Threading.ManualResetEventSlim evtPrepare = new System.Threading.ManualResetEventSlim(false);
            System.Threading.Thread thdRender = new System.Threading.Thread(() =>
            {
                _dspRender = Dispatcher.CurrentDispatcher;
                evtPrepare.Set();
                Dispatcher.Run();
            });
            thdRender.Start();
            evtPrepare.Wait();

            _dspRender.BeginInvoke(new Action(() =>
            {
                // DX11
                DX11ImportLib_GlobalMethods.D3D11CreateDevice(ppImmediateContext : out pDC, ppDevice : out pDevice);

                pDevice.CreateTexture2D(new D3D11_TEXTURE2D_DESC()
                {
                    Width = 1920,
                    Height = 1080,
                    MipLevels = 1,
                    ArraySize = 1,
                    Format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
                    SampleDesc = new DXGI_SAMPLE_DESC()
                    {
                        Count = 1,
                        Quality = 0,
                    },
                    Usage = 0,
                    BindFlags = D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET | D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE,
                    CPUAccessFlags = 0,
                    MiscFlags = D3D11_RESOURCE_MISC_FLAG.D3D11_RESOURCE_MISC_SHARED,
                }, out pBackBuffer);
                pDevice.CreateRenderTargetView(pBackBuffer, out pRTV);
                pDC.OMSetRenderTargets(pRTV);

                D3D11_VIEWPORT vp;
                vp.Width = 1920;
                vp.Height = 1080;
                vp.MinDepth = 0.0f;
                vp.MaxDepth = 1.0f;
                vp.TopLeftX = 0;
                vp.TopLeftY = 0;
                pDC.RSSetViewports(vp);

                IDXGIResource pRes = (IDXGIResource)pBackBuffer;
                IntPtr hShared;
                pRes.GetSharedHandle(out hShared);

                Dispatcher.Invoke(new Action(() =>
                {
                    // DX9
                    WindowInteropHelper helper = new WindowInteropHelper(this);

                    IDirect3D9Ex pD9Ex;
                    DX11ImportLib_GlobalMethods.Direct3DCreate9Ex(DX11_Guids_And_Consts.D3D_SDK_VERSION, out pD9Ex);
                    IDirect3DDevice9Ex pD9Device;
                    IDirect3DTexture9 pD9Text;
                    IDirect3DSurface9 pD9Surf;
                    D3DPRESENT_PARAMETERS[] pD9PresentParam = new D3DPRESENT_PARAMETERS[1];
                    pD9PresentParam[0].BackBufferWidth = 1920;
                    pD9PresentParam[0].BackBufferHeight = 1080;
                    pD9PresentParam[0].Windowed = 1;
                    pD9PresentParam[0].SwapEffect = D3DSWAPEFFECT.D3DSWAPEFFECT_DISCARD;
                    pD9PresentParam[0].hDeviceWindow = helper.Handle;
                    pD9PresentParam[0].PresentationInterval = D3DPRESENT.D3DPRESENT_INTERVAL_IMMEDIATE;
                    pD9Ex.CreateDeviceEx(0, D3DDEVTYPE.D3DDEVTYPE_HAL, helper.Handle,
                        D3DCREATE.D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE.D3DCREATE_MULTITHREADED | D3DCREATE.D3DCREATE_FPU_PRESERVE, pD9PresentParam, null, out pD9Device);
                    pD9Device.CreateTexture(1920, 1080, 1, D3DUSAGE.D3DUSAGE_RENDERTARGET, D3DFORMAT.D3DFMT_A8R8G8B8, D3DPOOL.D3DPOOL_DEFAULT, out pD9Text, hShared);
                    pD9Text.GetSurfaceLevel(0, out pD9Surf);

                    IntPtr ptrD9Surf = Marshal.GetComInterfaceForObject(pD9Surf, typeof(IDirect3DSurface9));
                    img.Lock();
                    img.SetBackBuffer(D3DResourceType.IDirect3DSurface9, ptrD9Surf);
                    img.Unlock();
                    Marshal.Release(ptrD9Surf);
                }));

                // Render Loop
                BeginRender();
            }));

            //Random ran = new Random();
            //_timTick = new System.Threading.Timer(state =>
            //{
            //    _dspRender.BeginInvoke(new Action(() =>
            //    {
            //        float[] ClearColor = new float[] { (float)ran.NextDouble(), (float)ran.NextDouble(), (float)ran.NextDouble(), 1.0f };// rgba
            //        pDC.ClearRenderTargetView(pRTV, ClearColor);
            //        pDC.Flush();
            //        Dispatcher.Invoke(new Action(() =>
            //        {
            //            img.Lock();
            //            img.AddDirtyRect(new Int32Rect(0, 0, 1920, 1080));
            //            img.Unlock();
            //        }));
            //    }));
            //}, null, 1000 / 60, 1000 / 60);
        }

        int _iRenderState = 0;

        private void BeginRender()
        {
            if (System.Threading.Interlocked.Exchange(ref _iRenderState, 1) > 0) return;
            _dspRender.BeginInvoke(new Action(() =>
            {
                RenderLoop();
            }));
        }

        private void StopRender()
        {
            System.Threading.Interlocked.Exchange(ref _iRenderState, 0);
        }

        private void RenderLoop()
        {
            float[] ClearColor = new float[] { (float)_ranColor.NextDouble(), (float)_ranColor.NextDouble(), (float)_ranColor.NextDouble(), 1.0f };// rgba
            pDC.ClearRenderTargetView(pRTV, ClearColor);
            pDC.Flush();
            Dispatcher.Invoke(new Action(() =>
            {
                img.Lock();
                img.AddDirtyRect(new Int32Rect(0, 0, 1920, 1080));
                img.Unlock();
            }));
            if (System.Threading.Interlocked.Exchange(ref _iRenderState, 0) <= 0) return;
            BeginRender();
        }

        protected override void OnClosed(EventArgs e)
        {
            if (pDC != null && _dspRender != null)
            {
                _dspRender.BeginInvoke(new Action(() =>
                {
                    pDC.ClearState();
                    pDC.Flush();
                    Dispatcher.ExitAllFrames();
                }));
            }
            base.OnClosed(e);
        }

        ID3D11Texture2D pBackBuffer = null;
        ID3D11Device pDevice = null;
        ID3D11DeviceContext pDC = null;
        //IDXGISwapChain pSwapChain = null;
        ID3D11RenderTargetView pRTV = null;

        Dispatcher _dspRender = null;
        //System.Threading.Timer _timTick = null;
        Random _ranColor = new Random();

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            BeginRender();
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            StopRender();
        }
    }

}
